/**
 * PermissionFilter.java
 */
package com.ozq.common.web;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ozq.common.dao.entity.SpringJson;
import com.ozq.common.util.CacheUtils;
import com.ozq.common.util.QQOauthUtils;
import com.ozq.common.util.SignAlgorithmUtils;

/**
 * <自定义权限过滤器> <权限处理，静态文件放过>
 * 
 * @author 欧增奇
 * @version [版本号, 2017年7月12日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Order(1)
@WebFilter(filterName = "permissionFilter", urlPatterns = "/*")
public class PermissionFilter implements Filter
{
    static Logger logger = LoggerFactory.getLogger(PermissionFilter.class);

    /**
     * json转换
     */
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 权限过滤器-销毁-调用
     */
    @Override
    public void destroy()
    {
        logger.debug("权限过滤器-销毁-调用");
    }

    /**
     * 权限过滤器-初始化-执行过滤
     * 
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException
    {
        HttpServletRequest request = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;
        FilterChain chain = (FilterChain) arg2;

        String uri = request.getRequestURI();

        logger.debug("url:{},uil:{}", request.getRequestURL(), uri);

        // 判断是否QQ回调，配置首页的回调(必带入code和state)
        if (StringUtils.equals(uri, "/") && !StringUtils.isAnyBlank(request.getParameter(QQOauthUtils.TYPE), request.getParameter(QQOauthUtils.STATE)))
        {
            request.getRequestDispatcher("login/qq/do").forward(request, response);
            return;
        }

        // 限制Ip访问，拦截添加请求（允许读取其他的配置）
        if (StringUtils.startsWithAny(uri, "/sys/add/", "/sys/update/") && StringUtils.endsWith(uri, "/do"))
        {
            String ip = getIpAddr(request);
            if (StringUtils.isBlank(ip))
            {
                sendError(response, new SpringJson(SpringJson.FAIL, "非法访问！"));
                return;
            }

            logger.debug("ip:{}", ip);

            String key = CacheUtils.IP_LIMIT + HashCodeBuilder.reflectionHashCode(ip + uri);
            Integer countKey = (Integer) CacheUtils.get(key, CacheUtils.IP_LIMIT);
            if (countKey == null)
            {
                countKey = 1;
                CacheUtils.put(key, countKey, CacheUtils.IP_LIMIT);

                // 验证用户合法性，有时间再来做
            } else
            {
                sendError(response, new SpringJson(SpringJson.FAIL, "限制访问！"));
                return;
            }
        }

        // 签名算法确认
        if (StringUtils.startsWithAny(uri, "/sys/ajaxListPage/", "/sys/ajaxList/", "/sys/update/", "/sys/add/") && StringUtils.endsWithAny(uri, "/do", "/to"))
        {
            String ip = getIpAddr(request);
            if (StringUtils.isBlank(ip))
            {
                sendError(response, new SpringJson(SpringJson.FAIL, "非法访问！"));
                return;
            }

            // 参数验证
            String timestamp = request.getParameter("ozq.ti");
            String token = request.getParameter("ozq.ke");
            String sign = request.getParameter("ozq.si");

            // 验证参数
            if (StringUtils.isAnyBlank(timestamp, token, sign))
            {
                sendError(response, new SpringJson(SpringJson.FAIL, "参数异常！"));
                return;
            }

            // 判断是否超过10分钟，超过10分钟无效
            long overdueTime = 10 * 60 * 1000;
            // 矫正时间误差3分钟
            long minusOverdueTime = -3 * 60 * 1000;
            long timestampTime = Long.valueOf(timestamp);
            long currentTime = new Date().getTime();
            long resultTime = currentTime - timestampTime;
            if (resultTime < minusOverdueTime || resultTime > overdueTime)
            {
                sendError(response, new SpringJson(SpringJson.FAIL, "已经过期(误报,请矫正本地时间)！"));
                return;
            }

            // 生成sign
            String createSign = SignAlgorithmUtils.createSignArray(request.getParameterMap(), !StringUtils.equalsIgnoreCase(request.getMethod(), "POST"));
            if (StringUtils.isEmpty(createSign) || !StringUtils.equals(StringUtils.stripEnd(sign, "="), StringUtils.stripEnd(createSign, "=")))
            {
                sendError(response, new SpringJson(SpringJson.FAIL, "签名错误！"));
                return;
            }

            // 验证token过期
            String tokenObject = (String) CacheUtils.get(CacheUtils.TOKEN + HashCodeBuilder.reflectionHashCode(ip), CacheUtils.TOKEN);
            if (StringUtils.isEmpty(tokenObject) || !StringUtils.equals(StringUtils.stripEnd(token, "="), StringUtils.stripEnd(tokenObject, "=")))
            {
                sendError(response, new SpringJson(SpringJson.FAIL, "已经过期(误报,请刷新当前页面)！"));
                return;
            }
        }

        chain.doFilter(request, response);
    }

    /**
     * 权限过滤器-初始化-成功
     */
    @Override
    public void init(FilterConfig arg0) throws ServletException
    {
        logger.debug("权限过滤器-初始化-成功");
    }

    /**
     * 发送错误信息
     * 
     * @param response
     * @throws IOException
     */
    private void sendError(HttpServletResponse response, SpringJson jsonObj) throws IOException
    {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out;
        try
        {
            out = response.getWriter();
            out.append(objectMapper.writeValueAsString(jsonObj));
        } catch (IOException e)
        {
            logger.error("error:{}\n {}", e.getMessage(), e.getStackTrace());
            response.sendError(500);
        }
    }

    /**
     * 获取客户端IP地址
     * 
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request)
    {
        String ip = request.getHeader("x-forwarded-for");
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip))
        {
            ip = request.getRemoteAddr();
            if (ip.equals("127.0.0.1"))
            {
                // 根据网卡取本机配置的IP
                InetAddress inet = null;
                try
                {
                    inet = InetAddress.getLocalHost();
                } catch (UnknownHostException e)
                {
                    logger.error("error:{}\n {}", e.getMessage(), e.getStackTrace());
                }
                ip = inet.getHostAddress();
            }
        }
        // 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
        if (ip != null && ip.length() > 15)
        {
            if (ip.indexOf(",") > 0)
            {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }
}
